﻿using Mapster;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Routing;
using Nantianmen.Core.Services;
using Nantianmen.Domain.Dtos;
using Nantianmen.Domain.Models;
using Nantianmen.Shared;
using System.Linq;

namespace Nantianmen.Endpoints.Clusters
{
    public class ClusterManagementEndpoints : IEndpoint
    {
        public IEndpointRouteBuilder MapEndpoints(IEndpointRouteBuilder builder)
        {
            var group = builder.MapGroup("/clusters")
                .WithParameterValidation()
                .WithOpenApi()
                .WithTags("Clusters");

            group
                .MapGet("/all", async (IClusterService service) =>
                {
                    var clusters = await service.GetClustersAsync();
                    return clusters.Select(c => c.Adapt<ClusterDto>()).OrderByDescending(x => x.UpdatedAt).ToList();
                })
                .WithName("Cluster_ListAll");

            group
                .MapGet("/detail/{id}", async (string id, IClusterService service) =>
                {
                    var cluster = await service.FindClusterAsync(id);
                    return cluster?.Adapt<ClusterDto>();
                })
                .WithName("Cluster_Detail");

            group
                .MapPost("/create", async (ClusterDto dto, IClusterService service) =>
                {
                    var cluster = dto.Adapt<Cluster>();
                    return await service.CreateClusterAsync(cluster);
                })
                .WithName("Cluster_Create");

            group
                .MapPost("/update", async (UpdateClusterRequest request, IClusterService service) =>
                {
                    var cluster = await service.FindClusterAsync(request.Id);

                    if (cluster == null)
                    {
                        throw new KeyNotFoundException(request.Id);
                    }

                    var destinations = await service.ListDestinationsAsync(cluster.Id);

                    var toAddDestinations = request.DestinationIds != null && request.DestinationIds.Any() ?
                        request.DestinationIds.Except(destinations.Select(x => x.Id)).ToList()
                        : [];

                    var toRemoveDestinations = destinations.Select(x => x.Id).Except(request.DestinationIds ?? []).ToList();

                    cluster.Remark = request.Remark;
                    cluster.LoadBalancingPolicy = request.LoadBalancingPolicy;
                    cluster.HealthCheck = request.HealthCheck;
                    cluster.SessionAffinity = request.SessionAffinity;
                    cluster.HttpClient = request.HttpClient;
                    cluster.HttpRequest = request.HttpRequest;
                    cluster.Metadata = request.Metadata;
                    
                    var result = await service.UpdateClusterAsync(cluster);

                    if(result.Succeeded == false)
                    {
                        return result;
                    }

                    if(toAddDestinations.Count > 0)
                    {
                        var resultOfAdd = await service.AddDestinationsAsync(cluster, toAddDestinations);
                        if(resultOfAdd.Succeeded == false)
                        {
                            return resultOfAdd;
                        }
                    }

                    if(toRemoveDestinations.Count > 0)
                    {
                        var resultOfRemove = await service.RemoveDestinationsAsync(cluster, toRemoveDestinations);
                        if(resultOfRemove.Succeeded == false)
                        {
                            return resultOfRemove;
                        }
                    }

                    return NantianmenResult.Success;
                })
                .WithName("Cluster_Update");

            group
                .MapPost("/remove/{clusterId}", async ([FromQuery] string clusterId, IClusterService service) =>
                {
                    return await service.RemoveClusterAsync(clusterId);
                })
                .WithName("Cluster_Remove");

            group
                .MapGet("/{id}/list-destinations", async (string id, IClusterService service) =>
                {
                    var destinations = await service.ListDestinationsAsync(id);
                    return destinations.Select(x => x.Id) ?? [];
                })
                .WithName("Cluster_ListDestinations");

            group
                .MapPost("/add-destinations", async (AddDestinationsToClusterRequest request, NantianmenErrorDescriber errorDescriber, IClusterService service) =>
                {
                    var cluster = await service.FindClusterAsync(request.ClusterId);

                    if (cluster is null)
                    {
                        return NantianmenResult.Failed(errorDescriber.ClusterNotFound(request.ClusterId));
                    }

                    return await service.AddDestinationsAsync(cluster, request.DestinationIds);
                })
                .WithName("Cluster_AddDestinations");

            group
                .MapPost("/remove-destinations", async (RemoveDestinationsFromClusterRequest request, NantianmenErrorDescriber errorDescriber, IClusterService service) =>
                {
                    var cluster = await service.FindClusterAsync(request.ClusterId);

                    if (cluster is null)
                    {
                        return NantianmenResult.Failed(errorDescriber.ClusterNotFound(request.ClusterId));
                    }

                    return await service.RemoveDestinationsAsync(cluster, request.DestinationIds);
                })
                .WithName("Cluster_RemoveDestinations");

            return builder;
        }
    }
}
